/*-------------------------------------------------------------------------
 *
 * geqo_selection.c
 *	  遗传查询优化器的线性选择方案
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/backend/optimizer/geqo/geqo_selection.c
 *
 *-------------------------------------------------------------------------
 */

/* 贡献者：
   =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
   *  Martin Utesch				 * 自动控制研究所		   *
   =							 = 矿业与技术大学 =
   *  utesch@aut.tu-freiberg.de  * 德国弗赖贝格				   *
   =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
 */

/* 这段代码来自 D. Whitley 的 Genitor 算法 */

/*************************************************************/
/*															 */
/*	Copyright (c) 1990										 */
/*	Darrell L. Whitley										 */
/*	计算机科学系									         */
/*	科罗拉多州立大学									     */
/*															 */
/*	特此允许复制本程序的全部或部分以进行自由分发。作者的姓名  */
/*	及本版权声明必须包含在任何复制中。 */
/* geqo_copy
 *
 *	 将一个基因复制到另一个
 *
 */
/*															 */
/*************************************************************/

#include "postgres.h"

#include <math.h>

#include "optimizer/geqo_copy.h"
#include "optimizer/geqo_random.h"
#include "optimizer/geqo_selection.h"

static int	fc_linear_rand(PlannerInfo *fc_root, int fc_max, double fc_bias);


/*
 * geqo_selection
 *	根据输入参数描述的偏差，从池中选择第一个和第二个基因
 */
void geqo_selection(PlannerInfo *fc_root, Chromosome *fc_momma, Chromosome *fc_daddy,
			   Pool *fc_pool, double fc_bias)
{
	int			fc_first,
				fc_second;

	fc_first = fc_linear_rand(fc_root, fc_pool->size, fc_bias);
	fc_second = fc_linear_rand(fc_root, fc_pool->size, fc_bias);

	/*
	 * 确保我们选择了不同的基因，但如果池的大小只有
	 * 一个，则无法做到。
	 */
	if (fc_pool->size > 1)
	{
		while (fc_first == fc_second)
			fc_second = fc_linear_rand(fc_root, fc_pool->size, fc_bias);
	}

	geqo_copy(fc_root, fc_momma, &fc_pool->data[fc_first], fc_pool->string_length);
	geqo_copy(fc_root, fc_daddy, &fc_pool->data[fc_second], fc_pool->string_length);
}

/*
 * linear_rand
 *	  生成介于0和输入最大值之间的随机整数
 *	  使用输入线性偏差
 *
 *	  偏差是线性分布的y截距
 *
 *	  概率分布函数为：f(x) = 偏差 - 2（偏差 - 1）x
 *			 偏差 = （第一个规则的概率）/ （中间规则的概率）
 */
static int fc_linear_rand(PlannerInfo *fc_root, int fc_pool_size, double fc_bias)
{
	double		fc_index;			/* 索引在0和pool_size之间 */
	double		fc_max = (double) fc_pool_size;

	/*
	 * geqo_rand()不应该返回1.0，但如果它返回，则我们将
	 * 从这个方程得到最大值，而我们需要0 <= 索引 < 最大值。
	 * 另外，四舍五入误差可能会产生略微超出范围的值；
	 * 特别是避免向sqrt()传递略小于0的值。如果我们得到一个坏值，请再试一次。
	 */
	do
	{
		double		fc_sqrtval;

		fc_sqrtval = (fc_bias * fc_bias) - 4.0 * (fc_bias - 1.0) * geqo_rand(fc_root);
		if (fc_sqrtval > 0.0)
			fc_sqrtval = sqrt(fc_sqrtval);
		fc_index = fc_max * (fc_bias - fc_sqrtval) / 2.0 / (fc_bias - 1.0);
	} while (fc_index < 0.0 || fc_index >= fc_max);

	return (int) fc_index;
}
